﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Diagnostics;
using Scheduler;
using CoursePack;
using ScheduleGrid;
using MyConcordiaAPI;


namespace TheSchedulator.Forms
{
    public partial class ScheduleViewer : Form
    {
        #region Data

        private ScheduleManager m_Scheduler;
        private BackgroundWorker worker;
        private Student m_Student;
        private Session m_Session;
        #endregion

        public ScheduleViewer(Student _Student,Session _Session,TreeNodeCollection _AllPossibleCourses, List<TreeNode> _StudentCourses, List<Scheduler.Constraint> _ConstraintsList)
        {
            InitializeComponent();
            this.m_Student = _Student;
            this.m_Session = _Session;

            this.m_Scheduler = new ScheduleManager(dayView1.StartDate, _Student, this.dayView1);
            this.splitContainer3.Panel2.BackColor = System.Drawing.Color.FromArgb(230, 237, 247);


            foreach (TreeNode node in _AllPossibleCourses)
            {
                TreeNode clone = (TreeNode)node.Clone();
                this.treeView1.Nodes.Add(clone);               
            }

            foreach (TreeNode node in _StudentCourses)
            {
                UserControls.CourseChoice courseChoice = new UserControls.CourseChoice(node);
                courseChoice.Size = new Size(this.flowLayoutPanel1.Width - 8, courseChoice.Size.Height);
                courseChoice.OnRemoveCourse += courseChoice_OnRemoveCourse;
                this.flowLayoutPanel1.Controls.Add(courseChoice);

                this.m_Scheduler.AddCoursesNode(courseChoice.Courses);
            }


            foreach(TimeConstraint constraint in _ConstraintsList)
            {
                this.m_Scheduler.AddConstraint(constraint);
                
            }

            // Generate a schedule if there are some courses added previously by the user
            if (this.flowLayoutPanel1.Controls.Count > 0)
                this.btnGenerateSchedule_Click(null,null);

        }

        public ScheduleViewer(Student _Student,Semester _Semester)
        {
            InitializeComponent();
            this.m_Student = _Student;
            this.m_Session = new Session(_Semester.Year,_Semester.Session);

            this.m_Scheduler = new ScheduleManager(dayView1.StartDate, _Student, this.dayView1);
            this.splitContainer3.Panel2.BackColor = System.Drawing.Color.FromArgb(230, 237, 247);


            //////////////////////////////////
            #region Loading Courses
            this.treeView1.Nodes.Clear();


            List<Course> Courses = new List<Course>();
            List<Department> Departments = new List<Department>();

            Departments.Add(this.m_Student.Degree.Department);
            Departments.Add(new Department(408, "ENGR", null));

            Courses = MySQLApi.GetCourses(this.m_Session, Departments);

            if (Courses.Count > 0)
            {

                TreeNode CourseNode = new TreeNode(Courses[0].ToString());
                TreeNode TutorialN, LabN, LectureN;

                foreach (Course course in Courses)
                {
                    string str1 = CourseNode.Text;
                    string str2 = course.ToString();
                    if (str1 != str2)
                    {
                        CourseNode = new TreeNode(course.ToString());
                        this.treeView1.Nodes.Add(CourseNode);
                    }

                    LectureN = new TreeNode(course.Lecture.ToString());
                    LectureN.Tag = course;

                    CourseNode.Nodes.Add(LectureN);


                    if (course.HasTutorial)
                    {
                        TutorialN = new TreeNode(course.Tutorial.ToString());
                        TutorialN.Tag = course;

                        if (course.HasLab)
                        {
                            LabN = new TreeNode(course.Lab.ToString());
                            LabN.Tag = course;
                            TutorialN.Nodes.Add(LabN);
                            LectureN.Nodes.Add(TutorialN);
                        }
                        else
                        {
                            LectureN.Nodes.Add(TutorialN);
                        }
                    }
                    else if (course.HasLab)
                    {
                        LabN = new TreeNode(course.Lab.ToString());
                        LabN.Tag = course;
                        LectureN.Nodes.Add(LabN);
                    }
                }
            }
            #endregion
            ////////////////////////////////////

            foreach (TreeNode node in _Semester.SavedSchedule.Courses)
            {
                UserControls.CourseChoice courseChoice = new UserControls.CourseChoice(node);
                courseChoice.Size = new Size(this.flowLayoutPanel1.Width - 8, courseChoice.Size.Height);
                courseChoice.OnRemoveCourse += courseChoice_OnRemoveCourse;
                this.flowLayoutPanel1.Controls.Add(courseChoice);

                this.m_Scheduler.AddCoursesNode(courseChoice.Courses);
            }


            foreach (TimeConstraint constraint in _Semester.SavedSchedule.Constraints)
            {
                this.m_Scheduler.AddConstraint(constraint);

            }

            // Generate a schedule if there are some courses added previously by the user
            if (this.flowLayoutPanel1.Controls.Count > 0)
                this.btnGenerateSchedule_Click(null, null);

            this.m_Scheduler.Current = _Semester.SavedSchedule.Current;

        }

        #region DayView Methods


        private void dayView1_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
                if (dayView1.SelectedAppointment != null)
                {
                    this.m_Scheduler.RemoveAppointment(dayView1.SelectedAppointment);
                    dayView1.Invalidate();
                }
        }


        private void dayView1_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {

                TimeConstraint constraint = new TimeConstraint(this.dayView1.SelectionStart.TimeOfDay, this.dayView1.SelectionEnd.TimeOfDay, dayView1.SelectionStart.DayOfWeek);

                this.m_Scheduler.AddConstraint(constraint);
                this.dayView1.SelectionStart = new DateTime();
                this.dayView1.SelectionEnd = new DateTime();

                this.m_Scheduler.DisplayAppointements();

            }
            else
            {
                if (this.dayView1.SelectedAppointment == null)
                {
                }
                else if (this.dayView1.SelectedAppointment.GetType() == typeof(AppointmentCourse))
                {
                    displayCourseInfo(((AppointmentCourse)this.dayView1.SelectedAppointment).Course);
                }
            }


        }


        #endregion

        #region TreeView with Courses
        private void treeView1_AfterExpand(object sender, TreeViewEventArgs e)
        {
            Course _Course = (Course)e.Node.Tag;


            if ((Course)e.Node.Tag != null)
            {

                if (this.m_Scheduler.CurrentSchedule.AddCourse(_Course))
                {


                    dayView1.Invalidate();

                    Rectangle rec = e.Node.Parent.Bounds;

                    rec.X -= 15;

                    this.treeView1.Invalidate(rec);

                }
            }
        }
        


        private void treeView1_BeforeCollapse(object sender, TreeViewCancelEventArgs e)
        {
            Course _Course = (Course)e.Node.Tag;

            if ((_Course != null) && (e.Node.Text == _Course.Lecture.ToString()))
            {

                if (this.m_Scheduler.CurrentSchedule.RemoveCourse(_Course))
                {
                    dayView1.Invalidate();

                    Rectangle rec = e.Node.Parent.Bounds;

                    rec.X -= 15;

                    this.treeView1.Invalidate(rec);
                }

            }
            else
            {
                foreach (TreeNode course in e.Node.Nodes)
                {
                    Course cc = (Course)course.Tag;

                    if (this.m_Scheduler.CurrentSchedule.RemoveCourse(cc))
                    {
                        dayView1.Invalidate();
                        e.Node.Collapse();
                        break;
                    }

                }

            }
        }

        private void treeView1_DrawNode(object sender, DrawTreeNodeEventArgs e)
        {

            Course course = (Course)e.Node.Tag;
            bool InSchedule = false;

            if (e.Node.Parent == null)
                foreach (TreeNode cc in e.Node.Nodes)
                {
                    if (this.m_Scheduler.CurrentSchedule.IsCourseIn((Course)cc.Tag))
                        InSchedule = true;
                }

            bool isOSDrawn = e.DrawDefault;
            Graphics g = e.Graphics;
            TreeNode tn = e.Node;
            Font DrawFont = treeView1.Font;
            Brush ForeBrush = new SolidBrush(Color.Black);
            Brush BackGroundBrush = new SolidBrush(e.Node.BackColor);

            Brush TextBackGroundBrush = BackGroundBrush;
            float baseX = e.Bounds.Left;
            float baseY = e.Bounds.Top;
            string stringToDraw = tn.Text;
            bool hasParent = (tn.Parent != null);
            bool hasNext = (tn.NextNode != null);
            bool hasPrev = (tn.PrevNode != null);

            float strX = baseX;
            float strY = baseY;
            float adjX = ((tn.Level + 1) * 24);
            strX += adjX;

            Rectangle UpLine = new Rectangle((int)(strX - 17), (int)(baseY), 2, 8);
            Rectangle DownLine = new Rectangle((int)(strX - 17), (int)(baseY + 8), 2, 8);
            Rectangle ChildBox = new Rectangle((int)(baseX - 16 + adjX + 2), (int)(baseY + 2), 12, 12);

            float tailStartX = baseX - 16 + adjX + 2 + 12;
            float tailStartY = baseY + 8;
            if (e.Bounds.Width != 0)
            {
                if ((e.State & TreeNodeStates.Hot) == TreeNodeStates.Hot)
                {//Hot tracking
                    DrawFont = new Font(DrawFont, FontStyle.Underline);
                }
                if ((e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected)
                {//change foregroudn/background
                    TextBackGroundBrush = new SolidBrush(SystemColors.InactiveBorder);
                    if (this.ActiveControl == sender)
                    {
                        ForeBrush = new SolidBrush(SystemColors.HighlightText);
                        TextBackGroundBrush = new SolidBrush(SystemColors.Highlight);
                    }
                }
                //// Erase previous
                g.FillRectangle(BackGroundBrush, e.Bounds);
                SizeF stringSize = g.MeasureString(stringToDraw, DrawFont, (int)(e.Bounds.Width - strX), new StringFormat(StringFormatFlags.NoWrap));
                //// Draw the "selected" background
                g.FillRectangle(TextBackGroundBrush, strX, strY, (float)Math.Floor(stringSize.Width), 16F);
                //// Draw the text
                g.DrawString(stringToDraw, DrawFont, ForeBrush, strX, strY);

                //// Every node gets a tail sticking out
                g.FillRectangle(Brushes.Black, strX - 17, tailStartY, 16, 2);
                ///////////////////////////////////////////////////////////////////
                if (hasPrev || hasParent) g.FillRectangle(Brushes.Black, UpLine);// Draw up line
                if (hasNext) g.FillRectangle(Brushes.Black, DownLine);// Draw down line
                //// Draw the "+" box
                if (tn.Nodes.Count > 0)
                {
                    if (InSchedule)
                        g.FillRectangle(new SolidBrush(Color.BlueViolet), ChildBox);

                    if (e.Node.Parent != null)
                        g.FillRectangle(new SolidBrush(Color.BlueViolet), ChildBox);

                    if (!tn.IsExpanded)
                        g.FillRectangle(new SolidBrush(Color.Red), ChildBox);

                    g.DrawRectangle(Pens.Black, ChildBox);
                }

            }//end of valid draw region

        }

        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            Course _Cc = (Course)e.Node.Tag;
            displayCourseInfo(_Cc);
        }
        
        #endregion

        #region Tab Pages Methods

        #endregion

        #region Form methods
        private void ScheduleViewer_Load(object sender, EventArgs e)
        {
            this.UpdateLabelGenerator();
        }
        #endregion

        #region BackgroundWorker procedures RATE MY PROFESSOR
        public delegate void UpdateTextCallback(RateMyProfessorResponse profRating);

        /// <summary>
        /// The work for the BackgroundWorker to perform.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void DoWork(object sender, DoWorkEventArgs e)
        {
            RateMyProfessorResponse rating = RateMyProfessor.GetProfessorRating((Course)e.Argument);

            this.lblTitle.BeginInvoke(new UpdateTextCallback(this.ratemyprof),
                rating);

        }

        private void ratemyprof(RateMyProfessorResponse rating)
        {

            if (rating.IsRatingAvailable)
            {
                string clarity = rating.Clarity;
                string helpfulness = rating.Helpfulness;
                string overall = rating.Overall;
                string easy = rating.Easiness;

                this.lblClarityN.Text = clarity;
                this.lblEasinessN.Text = easy;
                this.lblHelpfulnessN.Text = helpfulness;
                this.lblOveralN.Text = overall;

            }
            else
            {
                this.lblClarityN.Text = "N/A";
                this.lblEasinessN.Text = "N/A";
                this.lblHelpfulnessN.Text = "N/A";
                this.lblOveralN.Text = "N/A";
            }
        }

        #endregion

        #region Buttons Methods
        /// <summary>
        /// Add CourseChoice to the list to generate schedule
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void vistaButton1_Click(object sender, EventArgs e)
        {
            if (this.treeView1.SelectedNode != null)
            {
                float total = 0;
                foreach (UserControls.CourseChoice cc in this.flowLayoutPanel1.Controls)
                {
                    total += cc.Credits;
                }

                if (total > this.m_Student.Degree.GetMaxCreditsAllowed(this.m_Session))
                {
                    this.UpdateInfoBulle("You have reached the maximum number of credits you can take for a single schedule",Color.DarkRed,Color.White);
                    return;
                }

                TreeNode node = this.treeView1.SelectedNode;
                while (node.Parent != null)
                {
                    node = node.Parent;
                }

                foreach (UserControls.CourseChoice cc in this.flowLayoutPanel1.Controls)
                {
                    if (cc.CourseName == node.Text.ToString())
                    {
                        this.UpdateInfoBulle("The course has already been added !", Color.Gold, Color.Black);
                        return;
                    }
                }

                UserControls.CourseChoice courseChoice = new UserControls.CourseChoice(node);
                courseChoice.OnRemoveCourse += courseChoice_OnRemoveCourse;

                this.m_Scheduler.AddCoursesNode(courseChoice.Courses);

                courseChoice.Size = new Size(this.flowLayoutPanel1.Width - 8, courseChoice.Size.Height);

                this.flowLayoutPanel1.Controls.Add(courseChoice);
            }
        }


        private void courseChoice_OnRemoveCourse(object sender, EventArgs e)
        {
            ((UserControls.CourseChoice)sender).OnRemoveCourse -= courseChoice_OnRemoveCourse;

            this.m_Scheduler.RemoveCoursesNode(((UserControls.CourseChoice)sender).Courses);

            this.flowLayoutPanel1.Controls.Remove((Control)sender);
        }

        #endregion

        private void btnBack_Click(object sender, EventArgs e)
        {
            this.m_Scheduler.Back();
            this.UpdateLabelGenerator();
        }

        /// <summary>
        /// Generate schedule based on added constraint
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnGenerateSchedule_Click(object sender, EventArgs e)
        {
            if (this.m_Scheduler.Generate())
            {
                this.UpdateLabelGenerator();
                this.UpdateInfoBulle(this.m_Scheduler.TotalNumber + " schedules have been generated. you can naviguate through them by clicking on << and >>.", Color.Green, Color.White);
            }
            else
            {
                this.m_Scheduler.DisplayAppointements();
                this.UpdateInfoBulle("Sorry, there are no possible schedules for your choice of courses.", Color.DarkRed, Color.White);
            }
        }

        private void btnNext_Click(object sender, EventArgs e)
        {
            this.m_Scheduler.Next();
            this.UpdateLabelGenerator();
        }

        private void UpdateLabelGenerator()
        {
            if (this.m_Scheduler.TotalNumber > 0)
                this.lblSchedules.Text = (this.m_Scheduler.Current + 1).ToString() + " / " + this.m_Scheduler.TotalNumber;
        }

        private void UpdateInfoBulle(string _Message,Color _BackColor,Color _ForeColor)
        {
            this.timer1.Stop();
            this.lblInfoBulle.Visible = true;
            this.lblInfoBulle.Text = _Message;
            this.lblInfoBulle.BackColor = _BackColor;
            this.lblInfoBulle.ForeColor = _ForeColor;
            this.timer1.Start();
        }

        private void btnSaveSchedule_Click(object sender, EventArgs e)
        {
            List<TreeNode> courselist = new List<TreeNode>();
            foreach(UserControls.CourseChoice cc in this.flowLayoutPanel1.Controls)
            {
                courselist.Add(cc.Course);
            }
            
            this.m_Student.Degree.CreateSemester(new SavedSchedule(courselist,this.m_Scheduler.ConstraintManager.Constraints,this.m_Scheduler.Current),this.m_Session);
  
        }

        private void flowLayoutPanel1_ControlAdded(object sender, ControlEventArgs e)
        {
            this.panel1.Enabled = true;
            this.UpdateInfoBulle("A new course has been added. You need to re-generate a schedule to see the changes.", Color.Gold,Color.Black);
        }

        private void flowLayoutPanel1_ControlRemoved(object sender, ControlEventArgs e)
        {
            if (this.flowLayoutPanel1.Controls.Count == 0)
                this.panel1.Enabled = false;

            this.UpdateInfoBulle("A course has been removed. You need to re-generate a schedule to see the changes.", Color.Gold,Color.Black);
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            this.lblInfoBulle.Visible = false;
            this.timer1.Stop();
        }

        private void displayCourseInfo(Course _Cc)
        {
            if (_Cc != null)
            {
                this.lblClarityN.Text = "";
                this.lblEasinessN.Text = "";
                this.lblHelpfulnessN.Text = "";
                this.lblOveralN.Text = "";

                // Tell the worker to report progress.
                this.lblTitle.Text = _Cc.CourseName + " " + _Cc.CourseNumber + " - " + _Cc.CourseTitle;

                worker = new BackgroundWorker();

                worker.WorkerReportsProgress = true;

                worker.DoWork += DoWork;
                worker.RunWorkerAsync(_Cc);

                this.lblProf.Text = _Cc.Lecture.Professor;

                this.lblOverall.Left = this.lblOveralN.Left;

                this.lblHelpfulnessN.Left = this.lblOveralN.Left + this.lblOveralN.Width + 10;
                this.lblHelpfulness.Left = this.lblHelpfulnessN.Left;

                this.lblClarityN.Left = this.lblHelpfulnessN.Left + this.lblHelpfulnessN.Width + 10;
                this.lblClarity.Left = this.lblClarityN.Left;


                this.lblEasinessN.Left = this.lblClarityN.Left + this.lblClarityN.Width + 10;
                this.lblEasiness.Left = this.lblEasinessN.Left;

                this.panel3.Visible = true;
            }
        }




        private void btnRegisterMyConcordia_Click_1(object sender, EventArgs e)
        {
            MyConcordiaLogin frmLogin = new MyConcordiaLogin();

            if (frmLogin.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                foreach (Course cc in this.m_Scheduler.CurrentSchedule.Courses)
                {
                    CourseManipulation courseMani = new CourseManipulation(cc, frmLogin.Credentials.Username,frmLogin.Credentials.Password);
                    courseMani.OnRequestStatusChanged += courseMani_OnRequestStatusChanged;
                    courseMani.AddCourse();
                }
            }
        }

        void courseMani_OnRequestStatusChanged(object sender, EventArgs e)
        {
            this.lblRegistrationStatus.Text = (string)sender;
        }
    }
}
    
